package com.wish233.wiliwiliuser.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wish233.wiliwiliapi.domain.dto.UserDTO;
import com.wish233.wiliwilicommon.common.ErrorCode;
import com.wish233.wiliwilicommon.constants.CommonConstant;
import com.wish233.wiliwilicommon.exception.BusinessException;
import com.wish233.wiliwilicommon.utils.SqlUtils;
import com.wish233.wiliwiliuser.domain.dto.UserQueryRequest;
import com.wish233.wiliwiliuser.domain.dto.UserUpdateRequest;
import com.wish233.wiliwiliuser.domain.po.User;
import com.wish233.wiliwiliuser.domain.vo.UserVO;
import com.wish233.wiliwiliuser.mapper.UserMapper;
import com.wish233.wiliwiliuser.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.wish233.wiliwilicommon.constants.RedisConstants.SIGN_IN_LOCK_KEY;
import static com.wish233.wiliwilicommon.constants.RedisConstants.SIGN_IN_LOCK_TTL;
import static com.wish233.wiliwiliuser.domain.constant.UserConstant.SALT;

/**
 * @author WHH
 * @description 针对表【user(用户表)】的数据库操作Service实现
 * @createDate 2025-02-26 14:22:45
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {


    @Resource
    private RedissonClient redissonClient;
    @Resource
    private UserMapper userMapper;


    @Override
    public UserVO userLogin(String username, String password, HttpServletRequest request) {
        // 1. 校验
        if (!StrUtil.isAllNotBlank(username, password)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        if (username.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号错误");
        }
        if (password.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
        }
        // 2. 加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes());
        // 查询用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        queryWrapper.eq("password", encryptPassword);
        User user = this.baseMapper.selectOne(queryWrapper);
        // 用户不存在
        if (user == null) {
            log.info("user login failed, userAccount cannot match userPassword");
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误");
        }
        // 3. 记录用户的登录态 sa-token登录
        LocalDateTime loginTime = user.getLoginTime();
        if (loginTime == null) {
            //如果第一次登录的话设置在两天前便于签到
            user.setLastLoginTime(LocalDate.now().minusDays(2).atStartOfDay());
        } else {
            user.setLastLoginTime(user.getLoginTime());
        }
        user.setLoginTime(LocalDateTime.now());
        updateById(user);
        StpUtil.login(user.getId().toString());
        return this.getLoginUserVO(user);
    }

    @Override

    public long userRegister(String username, String password, String checkPassword) {

        // 1. 校验
        if (!StrUtil.isAllNotBlank(username, password, checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        if (username.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号过短");
        }
        if (password.length() < 8 || checkPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短");
        }
        // 密码和校验密码相同
        if (!password.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        }
        synchronized (username.intern()) {
            // 账户不能重复
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("username", username);
            long count = this.baseMapper.selectCount(queryWrapper);
            if (count > 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复");
            }
            // 2. 加密
            String encryptPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes());
            // 3. 插入数据
            User user = new User();
            user.setUsername(username);
            user.setPassword(encryptPassword);
            user.setNickname("用户" + RandomUtil.randomString(6));
            boolean saveResult = this.save(user);
            if (!saveResult) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败，数据库错误");
            }
            return user.getId();
        }

    }

    @Override
    public Boolean userSign() {
        //查询当前用户上一次登录时间，如果是上次登录时间在现在时间之前，那就可以签到
        Long loginId = Long.parseLong((String) StpUtil.getLoginId());
        User user = getById(loginId);
        LocalDate localDate = user.getLastLoginTime().toLocalDate();
        //和现在时间对比
        if (localDate.isBefore(LocalDate.now())) {
            // 获取分布式锁对象
            RLock lock = redissonClient.getLock(SIGN_IN_LOCK_KEY + user.getUsername());
            try {
                // 尝试加锁，最多等待 1 秒，锁过期时间为 5 秒
                boolean isLocked = lock.tryLock(1, SIGN_IN_LOCK_TTL, TimeUnit.SECONDS);
                if (isLocked) {
                    // 加锁成功，签到
                    //如果上次登录时间是现在登录时间的前一天，视为连续登录
                    LocalDate pre = user.getLoginTime().toLocalDate().minusDays(1);
                    Integer loginDays = user.getLoginDays();
                    if (pre.getDayOfYear() == localDate.getDayOfYear()) {
                        loginDays++;
                    } else {
                        loginDays = 1;
                    }
                    int addDiamond = Math.min(loginDays * 5, 50);
                    user.setDiamond(user.getDiamond() + addDiamond);
                    //为用户增加钻石
                    user.setLoginDays(loginDays);
                    user.setLastLoginTime(user.getLoginTime());
                    updateById(user);
                    return true;
                } else {
                    // 加锁失败
                    throw new BusinessException(ErrorCode.OPERATION_ERROR, "请勿重复签到");
                }
            } catch (InterruptedException e) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "请勿重复签到");
            } finally {
                // 释放锁
                if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
        return false;
    }

    @Override
    public UserVO getUserById(Long id) {
        if (id == null || id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        User user = getById(id);
        if (user == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "用户不存在");
        }
        return convertToLoginUserVO(user);
    }

    @Override
    public boolean updateUser(UserUpdateRequest userUpdateRequest) {
        if (userUpdateRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long userId = userUpdateRequest.getId();
        if (userId == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "用户不存在");
        }
        User user = getById(userId);
        if (user == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "用户不存在");
        }
        // 更新用户信息
        user.setNickname(userUpdateRequest.getNickname());
        user.setPhone(userUpdateRequest.getPhone());
        user.setAvatar(userUpdateRequest.getAvatar());
        user.setUpdateTime(new Date());
        return updateById(user);
    }

    @Override
    public boolean deleteUser(Long id) {
        if (id == null || id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        return removeById(id);
    }

    private UserVO convertToLoginUserVO(User user) {
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        return userVO;
    }

    /**
     * 获取用户的VO
     *
     * @param user
     * @return
     */
    private UserVO getLoginUserVO(User user) {
        if (user == null) {
            return null;
        }
        return BeanUtil.copyProperties(user, UserVO.class);
    }

    @Override
    public QueryWrapper<User> getQueryWrapper(UserQueryRequest userQueryRequest) {
        if (userQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }

        Long id = userQueryRequest.getId();
        String username = userQueryRequest.getUsername();
        String nickname = userQueryRequest.getNickname();
        String phone = userQueryRequest.getPhone();
        String sortField = userQueryRequest.getSortField();
        String sortOrder = userQueryRequest.getSortOrder();
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(id != null, "id", id);
        queryWrapper.eq(StrUtil.isNotBlank(username), "username", username);
        queryWrapper.like(StrUtil.isNotBlank(nickname), "nickname", nickname);
        queryWrapper.like(StrUtil.isNotBlank(phone), "phone", phone);
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    @Override
    public UserDTO getUserDTOById(Long id) {
        User user = getById(id);
        return BeanUtil.copyProperties(user, UserDTO.class);
    }

    @Override
    public Map<Long, UserDTO> getUserByIdBatch(Set<Long> userIds) {
        List<User> users = userMapper.selectBatchIds(userIds);
        return users.stream()
                .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
                .collect(Collectors.toMap(
                        UserDTO::getId,
                        a -> a,
                        //第一个第二个相同取第一个
                        (k1, k2) -> k1
                ));
    }

    @Override
    public List<UserDTO> getUserByIdBatchSorted(Set<Long> userIds) {
        return userMapper.selectUsersByIdsSorted(userIds).stream()
                .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
                .collect(Collectors.toList());
    }
}




